﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.Collections;

namespace Gauss.Isp
{
    public class Transitions
    {
        #region Member Variables

        private int _numRanks;
        private List<List<Envelope>> _transitions;
        private int _currentInterleaving;
        private int _currentTransition;
        private bool _deadlock;
        private bool _assert;

        #endregion

        #region Properties

        public int Ranks
        {
            get { return _numRanks; }
        }

        public int TotalInterleavings
        {
            get { return _transitions.Count; }
        }

        public int CurrentInterleaving
        {
            get { return _currentInterleaving + 1; }
        }

        public bool Deadlock
        {
            get { return _deadlock; }
        }

        public bool AssertionFailure
        {
            get { return _assert; }
        }

        #endregion

        #region Constructor

        public Transitions(string logFilePath, bool useIssueOrder)
        {
            _numRanks = -1;
            _transitions = new List<List<Envelope>>();
            _deadlock = false;
            _assert = false;

            // This is an index of "interleaving_rank_index" of the Envelope. It contains
            // the envelopes that have a matching point-to-point operation, that have not
            // been paired up yet.
            Dictionary<string, Envelope> p2pMatches = new Dictionary<string, Envelope>();
            
            // The collective is a little harder. The 'collectiveCount' is a list that for
            // each rank, it says how many mpi calls of that name it has seen with the specific
            // communicator. So each rank, the dictionary will store something like:
            //   MPI_Barrier 0 => 5
            // Meaning it has seen 5 MPI_Barrier's with communicator 0 so far for that rank.
            // Then 'collectiveMatches' stores the Envelopes in which their collective lists have
            // not been fully populated - so they can be added to when more Envelopes are parsed.
            // It is the format: MPI_Call Count RanksInvolved => List(Envelope)
            Dictionary<string, List<Envelope>> collectiveMatches = new Dictionary<string, List<Envelope>>();
            List<Dictionary<string, int>> collectiveCount = new List<Dictionary<string, int>>();

            Dictionary<int, int> issueOrders = new Dictionary<int, int>();
            int interleaving = -1;
            using (StreamReader sr = new StreamReader(logFilePath))
            {
                string line = sr.ReadLine();
                if (!Utils.TryParseInt(line, out _numRanks))
                {
                    throw new InvalidDataException(Resources.IspParser_CouldNotFindProcessCount);
                }
                else
                {
                    for (int i = 0; i < _numRanks; i++)
                    {
                        collectiveCount.Add(new Dictionary<string, int>());
                    }
                }

                line = sr.ReadLine();
                while (line != null)
                {
                    if (line.EndsWith("DEADLOCK"))
                    {
                        _deadlock = true;
                        break;
                    }

                    Envelope e = Envelope.Parse(line);
                    if (e == null)
                    {
                        throw new InvalidDataException(Utils.StringFormat(Resources.IspParser_InvalidParse, line));
                    }
                    else if (e.Assert)
                    {
                        _assert = true;
                        e.Interleaving = (interleaving < 0) ? 1 : interleaving;
                    }
                    else if (e.MpiCallName == "MPI_assert")
                    {
                        line = sr.ReadLine();
                        continue;
                    }
                    else if (e.Leak)
                    {
                        line = sr.ReadLine();
                        continue;
                    }

                    // Advance to the next interleaving if needed.
                    if (e.Interleaving != interleaving)
                    {
                        _transitions.Add(new List<Envelope>());
                        interleaving = e.Interleaving;
                        for (int i = 0; i < _numRanks; i++)
                        {
                            collectiveCount[i].Clear();
                        }
                        p2pMatches.Clear();
                        collectiveMatches.Clear();
                    }

                    // Must fix the issue orders. If we have already seen, set to -1 meaning not issued.
                    if (issueOrders.ContainsKey(e.IssueIndex))
                        e.IssueIndex = -1;
                    else
                        issueOrders.Add(e.IssueIndex, 0);

                    // See if there is a P2P match - the dictionary contains the Envelope's interleaving,
                    // rank, and index. If there is a match, then store it in the envelopes. This is
                    // just so we don't have to search through everything later when we need the match.
                    if (e.MatchIndex >= 0 && e.MatchRank >= 0)
                    {
                        string p2pMatchString = e.Interleaving + "_" + e.MatchRank + "_" + e.MatchIndex;
                        if (p2pMatches.ContainsKey(p2pMatchString))
                        {
                            Envelope match = p2pMatches[p2pMatchString];
                            e.PairWithEnvelope(match);
                            if (e.MpiCallName != "MPI_Probe" && e.MpiCallName != "MPI_Iprobe")
                                p2pMatches.Remove(p2pMatchString);
                        }
                        else
                        {
                            p2pMatches[e.Interleaving + "_" + e.Rank + "_" + e.Index] = e;
                        }
                    }
                    
                    // Next, see if there is a collective match.
                    // Also, don't want to match for communicators of size one,
                    // since there will be nothing to add.
                    if (e.Communicator >= 0 && e.CommunicatorRanks.Count > 1 && e.CommunicatorRanksString != null)
                    {
                        int count;
                        string collective = e.MpiCallName + " " + e.Communicator;
                        if (collectiveCount[e.Rank].ContainsKey(collective))
                            count = collectiveCount[e.Rank][collective];
                        else
                            count = 0;
                        collectiveCount[e.Rank][collective] = count + 1;

                        // Now see if there are others with the same collective.
                        collective = e.MpiCallName + " " + count + " " + e.CommunicatorRanksString;
                        if (collectiveMatches.ContainsKey(collective))
                        {
                            List<Envelope> matches = collectiveMatches[collective];
                            foreach (Envelope env in matches)
                            {
                                env.AddCollectiveMatch(e);
                            }
                            matches.Add(e);

                            // See if we have all of the matches, and if so, remove.
                            if (matches.Count == e.CommunicatorRanks.Count)
                            {
                                collectiveMatches.Remove(collective);
                            }
                        }
                        else
                        {
                            List<Envelope> matches = new List<Envelope>();
                            matches.Add(e);
                            collectiveMatches[collective] = matches;
                        }
                    }

                    // Add to the list.
                    _transitions[_transitions.Count - 1].Add(e);
                    
                    line = sr.ReadLine();
                }
            }

            // If there were no transitions in the interleaving, add at least one.
            if (_transitions.Count == 0)
                _transitions.Add(new List<Envelope>());

            // Sort the transitions as specified.
            if (useIssueOrder)
            {
                for (int i = 0; i < _transitions.Count; i++)
                {
                    _transitions[i].Sort(
                        delegate(Envelope a, Envelope b)
                        {
                            if (a.IssueIndex == -1 && b.IssueIndex == -1)
                                return a.OrderIndex.CompareTo(b.OrderIndex);
                            else if (a.IssueIndex == -1)
                                return 1;
                            else if (b.IssueIndex == -1)
                                return -1;
                            else
                                return a.IssueIndex.CompareTo(b.IssueIndex);
                        });
                }
            }
            else
            {
                for (int i = 0; i < _transitions.Count; i++)
                {
                    _transitions[i].Sort(
                        delegate(Envelope a, Envelope b)
                        {
                            if (a.OrderIndex == b.OrderIndex)
                            {
                                if (a.Rank != b.Rank)
                                    return a.Rank.CompareTo(b.Rank);
                                else
                                    return a.Index.CompareTo(b.Index);
                            }
                            else
                                return a.OrderIndex.CompareTo(b.OrderIndex);
                        });
                }
            }

            _currentInterleaving = 0;
            _currentTransition = -1;
        }

        #endregion

        #region Interleaving Functions

        public bool HasPreviousInterleaving
        {
            get
            {
                lock (this)
                {
                    return _currentInterleaving != 0;
                }
            }
        }

        public bool HasNextInterleaving
        {
            get
            {
                lock (this)
                {
                    return _currentInterleaving + 1 < _transitions.Count;
                }
            }
        }

        public bool PreviousInterleaving()
        {
            lock (this)
            {
                if (_currentInterleaving == 0)
                {
                    return false;
                }
                else
                {
                    _currentInterleaving--;
                    _currentTransition = -1;
                    return true;
                }
            }
        }

        public bool NextInterleaving()
        {
            lock (this)
            {
                if (_currentInterleaving + 1 >= _transitions.Count)
                {
                    return false;
                }
                else
                {
                    _currentInterleaving++;
                    _currentTransition = -1;
                    return true;
                }
            }
        }

        #endregion

        #region Transition Functions

        public Envelope StepToFirstTransition(int rank)
        {
            lock (this)
            {
                bool valid = true;
                if (rank < 0 || rank > _numRanks) valid = false;
                for (int i = 0; i < _transitions[_currentInterleaving].Count; i++)
                {
                    Envelope env = _transitions[_currentInterleaving][i];
                    if (!valid || env.Rank == rank)
                    {
                        _currentTransition = i;
                        return env;
                    }
                }
                return null;
            }
        }

        public Envelope StepToLastTransition(int rank)
        {
            lock (this)
            {
                bool valid = true;
                if (rank < 0 || rank > _numRanks) valid = false;
                for (int i = _transitions[_currentInterleaving].Count - 1; i >= 0; i--)
                {
                    Envelope env = _transitions[_currentInterleaving][i];
                    if (!valid || env.Rank == rank)
                    {
                        _currentTransition = i;
                        return env;
                    }
                }
                return null;
            }
        }

        public bool HasPreviousTransition
        {
            get
            {
                lock (this)
                {
                    return _currentTransition > 0;
                }
            }
        }

        public bool HasNextTransition
        {
            get
            {
                lock (this)
                {
                    return _currentTransition + 1 < _transitions[_currentInterleaving].Count;
                }
            }
        }

        public Envelope PreviousTransition(int rank)
        {
            lock (this)
            {
                if (_currentTransition <= 0)
                    return null;

                // If the rank is invalid, treat it as any rank can be changed.
                if (rank < 0 || rank >= _numRanks)
                {
                    _currentTransition--;
                    return _transitions[_currentInterleaving][_currentTransition];
                }
                else
                {
                    for (int i = _currentTransition - 1; i >= 0; i--)
                    {
                        Envelope e = _transitions[_currentInterleaving][i];
                        if (e.Rank == rank)
                        {
                            _currentTransition = i;
                            return e;
                        }
                    }
                    return null;
                }
            }
        }

        public Envelope NextTransition(int rank)
        {
            lock (this)
            {
                if (_currentTransition + 1 >= _transitions[_currentInterleaving].Count)
                    return null;

                // If the rank is invalid, treat it as any rank can advance.
                if (rank < 0 || rank >= _numRanks)
                {
                    _currentTransition++;
                    return _transitions[_currentInterleaving][_currentTransition];
                }
                else
                {
                    for (int i = _currentTransition + 1; i < _transitions[_currentInterleaving].Count; i++)
                    {
                        Envelope e = _transitions[_currentInterleaving][i];
                        if (e.Rank == rank)
                        {
                            _currentTransition = i;
                            return e;
                        }
                    }
                    return null;
                }
            }
        }

        public Envelope GetCurrentTransition()
        {
            if (_currentInterleaving < 0 || _currentInterleaving >= _transitions.Count ||
                _currentTransition < 0 || _currentTransition >= _transitions[_currentInterleaving].Count)
            {
                return null;
            }
            else
            {
                return _transitions[_currentInterleaving][_currentTransition];
            }
        }

        public Envelope SetCurrentTransition(Envelope envelope)
        {
            if (envelope == null)
                return null;

            if (envelope.Interleaving - 1 < 0 || envelope.Interleaving - 1 >= _transitions.Count)
                return null;

            for (int i = 0; i < _transitions[envelope.Interleaving - 1].Count; i++)
            {
                if (_transitions[envelope.Interleaving - 1][i] == envelope)
                {
                    _currentInterleaving = envelope.Interleaving - 1;
                    _currentTransition = i;
                    return envelope;
                }
            }
            return null;
        }

        #endregion

        #region Get Transition Matches

        public Envelope GetMatchingTransition(Envelope e)
        {
            if (e.IssueIndex < 0)
                return null;
            else
                return e.PointToPointMatch;
        }

        public List<Envelope> GetCollectiveTransitions(Envelope e)
        {
            if (e.IssueIndex < 0)
                return null;
            else
                return e.CollectiveMatches;
        }

        #endregion

        public IEnumerable<Envelope> GetEnvelopesInInterleaving(int interleaving)
        {
            if (interleaving < 1 || interleaving > TotalInterleavings)
                return null;
            else
                return _transitions[interleaving-1];
        }
    }

    #region Envelope

    public class Envelope
    {
        private string _stringOutput;

        private int _interleaving;
        private int _rank;
        private int _index;
        private int _order;
        private int _issue;
        private string _func_name;
        private string _filename;
        private int _linenumber;

        // For point-2-point matches.
        private int _match_rank;
        private int _match_index;
        private Envelope _match_envelope;

        // For collectives, sends, and receives.
        private int _communicator;
        private List<int> _communicator_ranks;
        private String _communicator_ranks_string;
        private List<Envelope> _communicator_matches;

        // For sends / receives.
        private int _src_rank;
        private int _dest_rank;
        private int _tag;

        // IntraCBs
        private List<CB> _intraCb;

        // InterCBs
        private List<CB> _interCb;

        // For outputting to string, determine what to output.
        private bool _communicator_set;
        private bool _src_rank_set;
        private bool _dest_rank_set;
        private bool _tag_set;

        // For assertions.
        private bool _assert;
        private string _assert_message;
        private string _assert_function;

        // For resource leaks.
        private bool _leak;
        private string _leak_resource;

        // Regular expressions for parsing.
        private static readonly Regex envelopeRegex = new Regex(@"^([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([-0-9]+)\s+([^\s]+)(?:\s+([^\s]+))?(?:\s+([^\s]+))?(?:\s+([^\s]+))?\s+{\s+([^}]*)}\s+{\s+([^}]*)}\s+Match:\s+([-]?[0-9]+)\s+([-]?[0-9]+)\s+File:\s+[-]?[0-9]+ (.+?) ([-]?[0-9]+)$", RegexOptions.Compiled);
        private static readonly Regex assertRegex = new Regex(@"^([0-9]+)\s+([0-9]+)\s+ASSERT[ ]+Message:[ ]+[0-9]+ (.+?) Function:[ ]+[0-9]+ (.+?) File:[ ]+[0-9]+ (.+?) ([-]?[0-9]+)$");
        private static readonly Regex leakRegex = new Regex(@"^([0-9]+)\s+([0-9]+)\s+Leak\s+(.+?)\s+{\s+}\s+{\s+}\s+Match:\s+-1\s+-1\sFile:[ ]+[0-9]+ (.+?) ([-]?[0-9]+)$");
        private static readonly Regex communicatorRegex = new Regex(@"^([0-9]+)_(?:([0-9]+):)*$", RegexOptions.Compiled);
        private static readonly Regex interCbRegex = new Regex(@"^(?:\[\s+([0-9]+)\s+([0-9]+)\s+\]\s+)*$", RegexOptions.Compiled);
        private static readonly Regex intraCbRegex = new Regex(@"^([0-9]+\s+)*$", RegexOptions.Compiled);

        public int Interleaving
        {
            get { return _interleaving; }
            set { _interleaving = value; }
        }

        public int Rank
        {
            get { return _rank; }
        }

        public bool SourceRankSet
        {
            get { return _src_rank_set; }
        }

        public int SourceRank
        {
            get { return _src_rank; }
        }

        public string MpiCallName
        {
            get { return _func_name; }
        }

        public int Index
        {
            get { return _index; }
        }

        public int OrderIndex
        {
            get { return _order; }
        }

        public int IssueIndex
        {
            get { return _issue; }
            set { _issue = value; }
        }

        public string FileName
        {
            get { return _filename; }
        }

        public int LineNumber
        {
            get { return _linenumber; }
        }

        public int MatchRank
        {
            get { return _match_rank; }
        }

        public int MatchIndex
        {
            get { return _match_index; }
        }

        public Envelope PointToPointMatch
        {
            get { return _match_envelope; }
        }

        public List<Envelope> CollectiveMatches
        {
            get { return _communicator_matches; }
        }

        public int Communicator
        {
            get { if (_communicator_set) return _communicator; else return -1; }
        }

        public string CommunicatorRanksString
        {
            get { return _communicator_ranks_string; }
        }

        public List<int> CommunicatorRanks
        {
            get { return _communicator_ranks; }
        }

        public bool Assert
        {
            get { return _assert; }
        }

        public string AssertMessage
        {
            get { return _assert_message; }
        }

        public bool Leak
        {
            get { return _leak; }
        }

        public string LeakResource
        {
            get { return _leak_resource; }
        }

        /// <summary>
        /// Only should be called by the Parse function.
        /// </summary>
        private Envelope()
        {
        }

        public void AddCollectiveMatch(Envelope e)
        {
            if (_communicator_matches == null)
                _communicator_matches = new List<Envelope>();
            _communicator_matches.Add(e);

            if (e._communicator_matches == null)
                e._communicator_matches = new List<Envelope>();
            e._communicator_matches.Add(this);
        }

        public void PairWithEnvelope(Envelope e)
        {
            this._match_envelope = e;
            e._match_envelope = this;
        }

        /// <summary>
        /// Parses the input line in to an envelope, or returns null if
        /// there was a parse error and an envelope couldn't be matched.
        /// </summary>
        /// <param name="line">The log line to parse.</param>
        /// <returns>An envelope representing the MPI function call.</returns>
        public static Envelope Parse(string line)
        {
            Match match = envelopeRegex.Match(line);
            if (!match.Success)
            {
                Match assertMatch = assertRegex.Match(line);
                if (assertMatch.Success)
                {
                    Envelope envelope = new Envelope();
                    envelope._assert = true;
                    envelope._issue = -1;
                    envelope._order = int.MaxValue;
                    envelope._index = int.MaxValue;
                    envelope._match_index = -1;
                    envelope._match_rank = -1;
                    envelope._func_name = "assert";
                    if (!Utils.TryParseInt(assertMatch.Groups[1].Value, out envelope._interleaving))
                        envelope._interleaving = -1;
                    if (!Utils.TryParseInt(assertMatch.Groups[2].Value, out envelope._rank))
                        envelope._rank = -1;
                    envelope._assert_message = assertMatch.Groups[3].Value;
                    envelope._assert_function = assertMatch.Groups[4].Value;
                    envelope._filename = assertMatch.Groups[5].Value;
                    if (!Utils.TryParseInt(assertMatch.Groups[6].Value, out envelope._linenumber))
                        envelope._linenumber = -1;
                    return envelope;
                }
                else 
                {
                    Match leakMatch = leakRegex.Match(line);
                    if (leakMatch.Success)
                    {
                        Envelope envelope = new Envelope();
                        envelope._leak = true;
                        envelope._issue = -1;
                        envelope._order = int.MaxValue;
                        envelope._index = int.MaxValue;
                        envelope._match_index = -1;
                        envelope._match_rank = -1;
                        envelope._func_name = "leak";
                        if (!Utils.TryParseInt(leakMatch.Groups[1].Value, out envelope._interleaving))
                            envelope._interleaving = -1;
                        if (!Utils.TryParseInt(leakMatch.Groups[2].Value, out envelope._rank))
                            envelope._rank = -1;
                        envelope._leak_resource = leakMatch.Groups[3].Value;
                        envelope._filename = leakMatch.Groups[4].Value;
                        if (!Utils.TryParseInt(leakMatch.Groups[5].Value, out envelope._linenumber))
                            envelope._linenumber = -1;
                        return envelope;
                    }
                    else
                    {
                        return null;
                    }
                }
            }

            Envelope env = new Envelope();
            for (int i = 1; i < match.Groups.Count; i++)
            {
                string value = match.Groups[i].Value;

                switch (i)
                {
                    case 1:
                        {
                            env._interleaving = Utils.ParseInt(value);
                            break;
                        }
                    case 2:
                        {
                            env._rank = Utils.ParseInt(value);
                            break;
                        }
                    case 3:
                        {
                            env._index = Utils.ParseInt(value);
                            break;
                        }
                    case 4:
                        {
                            env._order = Utils.ParseInt(value);
                            break;
                        }
                    case 5:
                        {
                            env._issue = Utils.ParseInt(value);
                            break;
                        }
                    case 6:
                        {
                            env._func_name = "MPI_" + value;
                            break;
                        }
                    case 7:
                        {
                            // If communicator, only first filled in.
                            if (!string.IsNullOrEmpty(value) && string.IsNullOrEmpty(match.Groups[i + 1].Value) && string.IsNullOrEmpty(match.Groups[i + 2].Value))
                            {
                                ParseCommunicator(env, value);
                                env._tag = 0;
                                env._tag_set = false;
                                env._src_rank = 0;
                                env._src_rank_set = false;
                                env._dest_rank = 0;
                                env._dest_rank_set = false;
                            }
                            // If all three, then a send or receive.
                            else if (!string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(match.Groups[i + 1].Value) && !string.IsNullOrEmpty(match.Groups[i + 2].Value))
                            {
                                if (env._func_name.ToLowerInvariant().Contains("recv") || env._func_name.ToLowerInvariant().Contains("probe"))
                                {
                                    env._src_rank = Utils.ParseInt(value);
                                    env._src_rank_set = true;
                                    env._dest_rank = 0;
                                    env._dest_rank_set = false;
                                }
                                else
                                {
                                    env._src_rank = 0;
                                    env._src_rank_set = false;
                                    env._dest_rank = Utils.ParseInt(value);
                                    env._dest_rank_set = true;
                                }
                                env._tag = Utils.ParseInt(match.Groups[i + 1].Value);
                                env._tag_set = true;
                                ParseCommunicator(env, match.Groups[i + 2].Value);
                            }
                            // Otherwise, a Wait, Test, or Finalize.
                            else
                            {
                                env._src_rank = 0;
                                env._src_rank_set = false;
                                env._dest_rank = 0;
                                env._dest_rank_set = false;
                                env._tag = 0;
                                env._tag_set = false;
                                env._communicator = 0;
                                env._communicator_set = false;
                            }
                            break;
                        }
                    case 10:
                        {
                            // IntraCBs
                            List<CB> intraCb = new List<CB>();
                            Match intraCbMatch = intraCbRegex.Match(value);
                            for (int j = 0; j < intraCbMatch.Groups[1].Captures.Count; j++)
                            {
                                string cb = intraCbMatch.Groups[1].Captures[j].Value;
                                if (cb != "")
                                    intraCb.Add(new CB(env._rank, Utils.ParseInt(cb)));
                            }
                            env._intraCb = intraCb;
                            break;
                        }
                    case 11:
                        {
                            // InterCBs
                            List<CB> interCb = new List<CB>();
                            Match interCbMatch = interCbRegex.Match(value);
                            for (int j = 0; j < interCbMatch.Groups[1].Captures.Count; j++)
                            {
                                string key = interCbMatch.Groups[1].Captures[j].Value;
                                string val = interCbMatch.Groups[2].Captures[j].Value;
                                if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(val))
                                    interCb.Add(new CB(Utils.ParseInt(key), Utils.ParseInt(val)));
                            }
                            env._interCb = interCb;
                            break;
                        }
                    case 12:
                        {
                            env._match_rank = Utils.ParseInt(value);
                            break;
                        }
                    case 13:
                        {
                            env._match_index = Utils.ParseInt(value);
                            break;
                        }
                    case 14:
                        {
                            env._filename = value;
                            break;
                        }
                    case 15:
                        {
                            env._linenumber = Utils.ParseInt(value);
                            break;
                        }
                }
            }
            return env;
        }

        public override string ToString()
        {
            if (_stringOutput != null)
                return _stringOutput;

            if (_assert)
            {
                Match m = new Regex("^Assertion `(.+?)' failed.$").Match(_assert_message);
                if (m.Success)
                    _stringOutput = Utils.StringFormat("{0}({1})", _func_name, m.Groups[1].Value);
                else
                    _stringOutput = Utils.StringFormat("{0}({1})", _func_name, _assert_message);
                return _stringOutput;
            }

            bool first = true;
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("{0}(", _func_name);

            if (_src_rank_set)
            {
                if (!first) sb.Append(", ");
                else first = false;
                if (_src_rank == -1)
                    sb.Append("source = MPI_ANY_SOURCE");
                else
                    sb.AppendFormat("source = {0}", _src_rank);
            }

            if (_dest_rank_set)
            {
                if (!first) sb.Append(", ");
                else first = false;
                sb.AppendFormat("dest = {0}", _dest_rank);
            }

            if (_tag_set)
            {
                if (!first) sb.Append(", ");
                else first = false;
                if (_tag == -1)
                    sb.Append("tag = MPI_ANY_TAG");
                else
                    sb.AppendFormat("tag = {0}", _tag);
            }

            if (_communicator_set)
            {
                if (!first) sb.Append(", ");
                else first = false;
                switch (_communicator)
                {
                    case 0:
                        sb.Append("comm = MPI_COMM_WORLD");
                        break;
                    case 1:
                        sb.Append("comm = MPI_COMM_SELF");
                        break;
                    case 2:
                        sb.Append("comm = MPI_COMM_NULL");
                        break;
                    default:
                        sb.AppendFormat("comm = {0}", _communicator);
                        break;
                }
            }

            sb.Append(")");

            _stringOutput = sb.ToString();
            return _stringOutput;
        }

        private static void ParseCommunicator(Envelope env, string value)
        {
            Match match = communicatorRegex.Match(value);
            if (!match.Success || match.Groups.Count == 1)
            {
                env._communicator_set = false;
                env._communicator = -1;
            }
            else
            {
                env._communicator_set = true;
                env._communicator = Utils.ParseInt(match.Groups[1].Value);
                env._communicator_ranks_string = value.Trim();
                if (match.Groups.Count > 2)
                {
                    env._communicator_ranks = new List<int>();
                    foreach (Capture c in match.Groups[2].Captures)
                    {
                        env._communicator_ranks.Add(Utils.ParseInt(c.Value));
                    }
                }
            }
        }
    }

    #endregion

    #region Comes Before

    class CB
    {
        private int _rank;
        private int _index;

        public CB(int rank, int index)
        {
            _rank = rank;
            _index = index;
        }

        public int Rank
        {
            get { return _rank; }
        }

        public int Index
        {
            get { return _index; }
        }
    }

    #endregion
}
